/**
 * 跨操作系统统一接口 for Linux
 */

#define _GNU_SOURCE
#define __USE_LARGEFILE64
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sched.h>
#include <sys/sysinfo.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/stat.h>

#include <stdio.h>
#include "unios.h"
#include "utils.h"

#include "log.h"
#define LOGLEVEL LOG_DEBUG
#define LOGTAG   "unios"
#include "log4app.h"

// ======================================================================================
// 基础功能
// ======================================================================================

void* unios_malloc(size_t size) { return malloc(size); }
void unios_free(void *ptr) { if(ptr) free(ptr); }
void unios_disable_interrupts() {}
void unios_enable_interrupts() {}
bool unios_is_in_isr() {return false; }

// ======================================================================================
// 关于时钟
// ======================================================================================

unios_us_t unios_us_now() {
    struct timespec tm = {0, 0};
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_sec * 1000000 + tm.tv_nsec / 1000;
}

void unios_us_delay(unios_us_t delay) {
	usleep(delay);
}

unios_ms_t unios_ms_now() {
    struct timespec tm = {0, 0};
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_sec * 1000 + tm.tv_nsec / 1000000;
}

void unios_ms_delay(unios_ms_t delay) {
	usleep(delay*1000);
}

unios_sec_t unios_rtcsec_get() {
    struct timespec tm = {0, 0};
    clock_gettime(CLOCK_REALTIME, &tm);
    return tm.tv_sec;
}

std_err_t unios_rtcsec_set(unios_sec_t sec) {
    struct timespec tm = {.tv_sec = sec, .tv_nsec = 0};
    return (clock_settime(CLOCK_REALTIME, &tm) == 0)? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_rtc_set(int year, int month, int day, int hour, int minute, int second) {
	struct tm dt = {0};
	dt.tm_year = year - 1900;
	dt.tm_mon = month - 1;
	dt.tm_mday = day;
	dt.tm_hour = hour;
	dt.tm_min = minute;
	dt.tm_sec = second;
	time_t rtcsec = mktime(&dt);
	return unios_rtcsec_set((unios_sec_t)rtcsec);
}

unios_datetime_t unios_rtc_local() {
    time_t rawtime = time(NULL);
    struct tm *st = localtime(&rawtime);
    unios_datetime_t tm = { 0 };
    tm.date.year = (uint16_t)st->tm_year; tm.date.month = (uint8_t)st->tm_mon; tm.date.day = (uint8_t)st->tm_mday; tm.date.weekday = (uint8_t)st->tm_wday;
    tm.time.hour = (uint8_t)st->tm_hour; tm.time.minute = (uint8_t)st->tm_min; tm.time.second = (uint8_t)st->tm_sec;
    return tm;
}

std_err_t unios_rtc_get(int *year, int *month, int *day, int *hour, int *minute, int *second, int *weekday) {
    unios_datetime_t dt = unios_rtc_local();
    if(year)    *year    = dt.date.year;
	if(month)   *month   = dt.date.month;
	if(day)     *day     = dt.date.day;
	if(weekday) *weekday = dt.date.weekday;
	if(hour)    *hour    = dt.time.hour;
	if(minute)  *minute  = dt.time.minute;
	if(second)  *second  = dt.time.second;
	return STD_ERR_OK;
}

char* unios_rtc_local_string() {
	static char str[64];
    time_t rawtime = time(NULL);
    struct tm *st = localtime(&rawtime);
	strftime(str, sizeof(str), "%c", st);
	return str;
}

std_err_t unios_rtc_timezone_set(const char *tz) {
    // 在 Linux/POSIX 系统中，tzset() 会读取 TZ 环境变量来设置时区
    setenv("TZ", tz, 1);
    tzset();
	return STD_ERR_OK;
}


// ======================================================================================
// 关于文件
// ======================================================================================

unios_file_t unios_file_null() {
    return -1;
}

bool unios_file_is_valid(unios_file_t uf) {
    return uf >= 0;
}

int64_t unios_file_size(const char *path) {
    struct stat fst;
    if(stat(path, &fst) < 0) { return -1; }
    return fst.st_size;
}

std_err_t unios_file_rename(const char *from, const char *to) {
    int err = rename(from, to);
    if(err) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

std_err_t unios_file_delete(const char *path) {
	int err = remove(path);
    if(err) { return STD_ERR_FAIL; }
    return STD_ERR_OK;
}

unios_file_t unios_file_from_stdio_stream(FILE *fp) {
    return fileno(fp);
}

std_err_t unios_file_open(const char *path, unios_file_oflag_t oflag, unios_file_t *uf) {
    if(!path || !uf) return STD_ERR_INVALID_ARG;
    *uf = unios_file_null();

    if(oflag == UNIOS_FILE_OFLAG_READ) {
    	*uf = open(path, O_RDONLY);
    } else if(oflag == UNIOS_FILE_OFLAG_APPEND) {
        // 注意: 此处必须结合使用 O_RDWR 标志，而不是 O_WRONLY 标志，否则数据不会写入！
        *uf = open(path, O_RDWR | O_APPEND | O_CREAT, 0644);
    } else if(oflag == UNIOS_FILE_OFLAG_WRITE_TRUNCATE) {
    	*uf = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0644);
    } else if(oflag == UNIOS_FILE_OFLAG_READ_WRITE) {
    	*uf = open(path, O_RDWR, 0644);
    } else if(oflag == UNIOS_FILE_OFLAG_DEVICE_IO) {
    	*uf = open(path, O_RDWR, 0644);
    } else {
    	return STD_ERR_INVALID_ARG;
    }

    if(unios_file_is_valid(*uf)) return STD_ERR_OK;
    return STD_ERR_FAIL;
}

void unios_file_destroy(unios_file_t *uf) {
    if(!uf || !unios_file_is_valid(*uf)) return;
    close(*uf);
    *uf = -1;
}

int64_t unios_file_datasize(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return -1;
    return (int64_t)lseek64(uf, 0, SEEK_END);
}

std_err_t unios_file_sync(unios_file_t uf) {
    if(!unios_file_is_valid(uf)) return STD_ERR_INVALID_ARG;
    return (fsync(uf) == 0) ? STD_ERR_OK : STD_ERR_FAIL;
}

std_err_t unios_file_read(unios_file_t uf, void *buf, size_t nbyte, int64_t offset, size_t *rbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    if(rbyte) *rbyte = 0;
    
    do {
        // 设置文件当前偏移
        if(offset >= 0 && lseek64(uf, offset, SEEK_SET) != (ssize_t)offset) {
            loge("lseek() failed: %s\n", strerror(errno));
            err = STD_ERR_FAIL; break;
        }

        ssize_t dlen = read(uf, buf, nbyte);
        if(rbyte) *rbyte = dlen;
        if(dlen != nbyte)  {
            loge("read() failed: %s\n", strerror(errno));
            err = STD_ERR_FAIL; break;
        }
    } while(0);

    return err;
}

std_err_t unios_file_write(unios_file_t uf, const void *buf, size_t nbyte, int64_t offset, size_t *wbyte) {
    if(!unios_file_is_valid(uf) || !buf || !nbyte) return STD_ERR_INVALID_ARG;
    std_err_t err = STD_ERR_OK;
    if(wbyte) *wbyte = 0;
    
    do {
        // 设置文件当前偏移
        if(offset >= 0 && lseek64(uf, offset, SEEK_SET) != (ssize_t)offset) {
            loge("lseek() failed: %s\n", strerror(errno));
            err = STD_ERR_FAIL; break;
        }

        ssize_t dlen = write(uf, buf, nbyte);
        if(wbyte) *wbyte = dlen;
        if(dlen != nbyte)  {
            loge("write() failed: %s\n", strerror(errno));
            err = STD_ERR_FAIL; break;
        }
    } while(0);

    return err;
}

void* unios_file_mmap(unios_file_t uf, uint64_t offset, uint64_t size, uint64_t flags1, uint64_t flags2, void **mmptr/*out*/, uint64_t *mmsize/*out*/) {
    if(!unios_file_is_valid(uf) || !size) return NULL;
    if(flags1 == 0) flags1 = PROT_READ | PROT_WRITE;
    if(flags2 == 0) flags2 = MAP_SHARED;
    void* mem = mmap(NULL, size, flags1, flags2, uf, offset);
    if(!mem || mem == MAP_FAILED) {
        loge("mmap(0x%08lX, 0x%08lX) failed: %s\n", offset, size, strerror(errno));
        return NULL;
    }
    if(*mmptr) *mmptr = mem;
    if(*mmsize) *mmsize = size;
    return mem;
}

void unios_file_munmap(void *mmptr, uint64_t mmsize) {
    if(!mmptr || !mmsize) return;
    if(mmptr == MAP_FAILED) return;
    munmap(mmptr, mmsize);
}

int unios_file_ioctl(unios_file_t uf, int cmd, void *opt, size_t opt_size) {
    if(!unios_file_is_valid(uf)) return -1;
    int ret = ioctl(uf, cmd, opt, opt_size);
    if(ret == -1) return -1;
    return ret;
}


// ======================================================================================
// 关于 CPU
// ======================================================================================

size_t unios_get_cpu_core_count() {
    int ret = get_nprocs();
    if(ret > 0) return ret;
    return 0;
}


// ======================================================================================
// 关于线程
// ======================================================================================

typedef void* (*linux_thread_func_t)(void *cookie);

std_err_t unios_thread_create(unios_thread_func_t func, void *cookie, unios_thread_priority_t priority, size_t stack_size, uint64_t cpu_affinity, unios_thread_t* pt) {
    if(!func || priority >= UNIOS_THREAD_PRIORITY_MAX) return STD_ERR_INVALID_ARG;

    size_t ncpu = 0;
    if(cpu_affinity) {
        // 获取 CPU 内核数量
        ncpu = unios_get_cpu_core_count();
        if(!ncpu) return STD_ERR_FAIL;
        if(cpu_affinity >= (uint64_t)(1ULL << ncpu)) return STD_ERR_OUT_OF_RANGE;
    }

	// 参考 https://blog.csdn.net/luopandeng/article/details/114329765
	static const int POLICY_MAP[] = {
		SCHED_OTHER, // THREAD_PRIORITY_NORMAL       , 标准调度策略，也是所谓 TSS 调度策略
		SCHED_IDLE,  // THREAD_PRIORITY_IDLE         , 只有 CPU idle 的时候才执行
		SCHED_IDLE,  // THREAD_PRIORITY_LOWEST       , 只有 CPU idle 的时候才执行
		SCHED_BATCH, // THREAD_PRIORITY_BELOW_NORMAL , 后台进程，优先级低于普通进程。不是会话型，不会根据休眠时间更改优先级。
		SCHED_OTHER, // THREAD_PRIORITY_NORMAL       , 标准调度策略，也是所谓 TSS 调度策略
		SCHED_RR,    // THREAD_PRIORITY_ABOVE_NORMAL , RR 是轮询的缩写，有确定的时间片(2.6.23 以前时间片是由优先级决定的, 之后使用固定的时间片 100ms)。时间片使用完时．执行权将转移到其他进程。
		SCHED_FIFO,  // THREAD_PRIORITY_HIGHEST      , 实时调度策略．即具有动态优先级(1-99)的调度策略。除了等待 I/O 完成时休眠、自发休眠或优先级更高的实时进程获得优先权以外，不会释放执行权。
		SCHED_FIFO,  // THREAD_PRIORITY_TIME_CRITICAL, 实时调度策略．即具有动态优先级(1-99)的调度策略。除了等待 I/O 完成时休眠、自发休眠或优先级更高的实时进程获得优先权以外，不会释放执行权。
	};
	static const int PRIORITY_MAP[] = {
		0 , // SCHED_OTHER, // THREAD_PRIORITY_NORMAL,
		0 , // SCHED_IDLE,  // THREAD_PRIORITY_IDLE,
		0 , // SCHED_IDLE,  // THREAD_PRIORITY_LOWEST,
		0 , // SCHED_BATCH, // THREAD_PRIORITY_BELOW_NORMAL,
		0 , // SCHED_OTHER, // THREAD_PRIORITY_NORMAL,
		30, // SCHED_RR,    // THREAD_PRIORITY_ABOVE_NORMAL,
		40, // SCHED_FIFO,  // THREAD_PRIORITY_HIGHEST,
		60, // SCHED_FIFO,  // THREAD_PRIORITY_TIME_CRITICAL,
	};
	int ret;
	std_err_t err = STD_ERR_OK;
	pthread_t thread = 0;
	pthread_attr_t attr;
	cpu_set_t cpuset;
	
	if(0 != pthread_attr_init(&attr)) return STD_ERR_FAIL;

	do {
		// 把线程配置为 DETACHED，这样就在线程结束时就不需要 pthread_join() 来释放资源了
		if(0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) { err = STD_ERR_FAIL; break; }
				   
		// 设置线程优先级
		if(priority != UNIOS_THREAD_PRIORITY_DEFAULT) {
			// 设置继承的调度策略为 PTHREAD_EXPLICIT_SCHED，否则设置的其它调度策略属性被忽略 !
			if(0 != pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { err = STD_ERR_FAIL; break; }
			// 设置线程调度策略
			if(0 != pthread_attr_setschedpolicy(&attr, POLICY_MAP[priority])) { err = STD_ERR_FAIL; break; }
			// 设置线程优先级
			struct sched_param param = { .sched_priority = PRIORITY_MAP[priority] };
			if(0 != pthread_attr_setschedparam(&attr, &param)) { err = STD_ERR_FAIL; break; }
		}

		// 创建线程
		if(0 != pthread_create(&thread, &attr, (linux_thread_func_t)func, cookie)) { err = STD_ERR_FAIL; break; }

		// 设置线程与 CPU 绑定关系
		if(cpu_affinity) {
			CPU_ZERO(&cpuset);
			ncpu = min(sizeof(cpu_affinity), unios_get_cpu_core_count());
			for(int i=0; i < ncpu; i++) { if(cpu_affinity & (1 << i)) CPU_SET(i, &cpuset); }
			if(0 != pthread_setaffinity_np(thread, sizeof(cpuset), &cpuset)) { err = STD_ERR_FAIL; break; }
		}

		#if 0
			if(0 == pthread_getaffinity_np(thread, sizeof(cpuset), &cpuset)) {
				char cpumap[sizeof(cpu_affinity) + 1];
				memset(cpumap, 0, sizeof(cpumap));
				for (int i = 0; i < ncpu; i++) { cpumap[i] = '0' + CPU_ISSET(i, &cpuset); }
				printf("thread created: %llu @ cpu[%s]\n", (FMTLLU)thread, cpumap);
			}
		#endif
	} while(0);

	pthread_attr_destroy(&attr);
	if(err) return err;
	if(pt) *pt = (unios_thread_t)thread;

    return STD_ERR_OK;
}

unios_thread_t unios_thread_this() {
    return (unios_thread_t)pthread_self();
}


// ======================================================================================
// 关于临界区
// ======================================================================================

std_err_t unios_critical_section_init(unios_critical_section_t* pcs) {
    if(!pcs) return STD_ERR_INVALID_ARG;

	pthread_mutex_t mutex0 = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_t *mutex = MALLOC0(pthread_mutex_t);
	if (!mutex) return STD_ERR_NO_MEM;
	memcpy(mutex, &mutex0, sizeof(pthread_mutex_t));
	*pcs = (unios_critical_section_t)mutex;

    return STD_ERR_OK;
}

std_err_t unios_critical_section_enter(unios_critical_section_t cs, long wait_timeout_ms) {
    if(!cs) return STD_ERR_INVALID_ARG;

    if(wait_timeout_ms < 0) return pthread_mutex_lock((pthread_mutex_t*)cs) ? STD_ERR_FAIL : STD_ERR_OK;
    int ret;
    unios_ms_t tms = unios_ms_now();
    while((ret = pthread_mutex_trylock((pthread_mutex_t*)cs)) != 0) {
        if(unios_ms_elapsed(tms) >= wait_timeout_ms) return STD_ERR_TIMEOUT;
        usleep(100);
    }

    return STD_ERR_OK;
}

void unios_critical_section_leave(unios_critical_section_t cs) {
    if(!cs) return;
    pthread_mutex_unlock((pthread_mutex_t*)cs);
}

void unios_critical_section_destroy(unios_critical_section_t *cs/*in,out*/) {
    if(!cs || !(*cs)) return;
    
    // 释放临界区
    unios_critical_section_leave((*cs));

    pthread_mutex_unlock((pthread_mutex_t*)(*cs));
    free((*cs));
    *cs = (unios_critical_section_t)NULL;
}


// ======================================================================================
// 关于锁
// ======================================================================================

std_err_t unios_lock_init(unios_lock_t* plock, size_t count) {
    if(!plock || count < 1) return STD_ERR_INVALID_ARG;

    pthread_mutex_t mutex0 = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t *mutex = MALLOC0(pthread_mutex_t);
    if (!mutex) return STD_ERR_NO_MEM;
    memcpy(mutex, &mutex0, sizeof(pthread_mutex_t));
    *plock = (unios_critical_section_t)mutex;

    return STD_ERR_OK;
}

std_err_t unios_lock(unios_lock_t lock, long wait_timeout_ms) {
    if(!lock) return STD_ERR_INVALID_ARG;

    if(wait_timeout_ms < 0) return pthread_mutex_lock((pthread_mutex_t*)lock) ? STD_ERR_FAIL : STD_ERR_OK;
    int ret;
    unios_ms_t tms = unios_ms_now();
    while((ret = pthread_mutex_trylock((pthread_mutex_t*)lock)) != 0) {
        if(unios_ms_elapsed(tms) >= wait_timeout_ms) return STD_ERR_TIMEOUT;
        usleep(100);
    }

    return STD_ERR_OK;
}

std_err_t unios_unlock(unios_lock_t lock) {
    if(!lock) return STD_ERR_INVALID_ARG;

    return pthread_mutex_unlock((pthread_mutex_t*)lock) ? STD_ERR_FAIL : STD_ERR_OK;
}

void unios_lock_destroy(unios_lock_t *lock) {
    if(!lock || !(*lock)) return;
    
    // 释放锁
    unios_unlock(*lock);

    pthread_mutex_unlock((pthread_mutex_t*)(*lock));
    // 释放内存
    free((*lock));
    *lock = NULL;
}


// ======================================================================================
// 关于事件
// ======================================================================================

typedef struct {
    bool state;
    bool manual_reset;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
} event_t;

std_err_t unios_event_init(unios_event_t* pevent, bool manual_reset, bool init_state) {
    if(!pevent) return STD_ERR_INVALID_ARG;

    event_t* hevent = MALLOC0(event_t);
    if(hevent == NULL) return STD_ERR_NO_MEM;
    hevent->state = init_state;
    hevent->manual_reset = manual_reset;

    if(pthread_mutex_init(&hevent->mutex, NULL)) {
        free(hevent);
        return STD_ERR_FAIL;
    }
    if(pthread_cond_init(&hevent->cond, NULL)) {
        pthread_mutex_destroy(&hevent->mutex);
        free(hevent);
        return STD_ERR_FAIL;
    }
    *pevent = hevent;

    return STD_ERR_OK;
}

std_err_t unios_event_set(unios_event_t event, bool signal) {
    if(!event) return STD_ERR_INVALID_ARG;

    std_err_t err;
    event_t* hevent = (event_t*)event;
    if(pthread_mutex_lock(&hevent->mutex) != 0) return STD_ERR_FAIL;
    hevent->state = signal;
    if(signal) {
        if(hevent->manual_reset) {
            err = pthread_cond_broadcast(&hevent->cond);
        } else {
            err = pthread_cond_signal(&hevent->cond);
        }
    }
    if(pthread_mutex_unlock(&hevent->mutex) != 0) return STD_ERR_FAIL;
    return err;
}

std_err_t unios_event_wait(unios_event_t event, long wait_timeout_ms) {
    if(!event) return STD_ERR_INVALID_ARG;

    event_t* hevent = (event_t*)event;
    int rc = 0;
    if(pthread_mutex_lock(&hevent->mutex) != 0) return STD_ERR_FAIL;

    // 未出错 且 无信号，就循环等待
    while(!rc && !hevent->state) {
        if(wait_timeout_ms <= 0) {
            // 永远等待？
            rc = pthread_cond_wait(&hevent->cond, &hevent->mutex);
        } else {
            // 带超时的等待，计算超时时刻
            struct timespec abstime;
            struct timeval tv;
            gettimeofday(&tv, NULL);
            abstime.tv_sec = tv.tv_sec + wait_timeout_ms / 1000;
            abstime.tv_nsec = tv.tv_usec * 1000 + (wait_timeout_ms % 1000) * 1000000;
            if(abstime.tv_nsec >= 1000000000) { abstime.tv_nsec -= 1000000000; abstime.tv_sec++; }
            // 带超时的等待
            rc = pthread_cond_timedwait(&hevent->cond, &hevent->mutex, &abstime);
        }
    }

    if(rc) {
        // 出错，表示超时无信号 或 遇到其它问题
        if(pthread_mutex_unlock(&hevent->mutex) != 0) return STD_ERR_FAIL;
        if(rc == ETIMEDOUT) return STD_ERR_TIMEOUT; // 超时无信号
        return STD_ERR_FAIL;                        // 遇到其它问题
    } else {
        // 未出错，表示有信号了
        if(!hevent->manual_reset) hevent->state = false;    // 自动复位模式下，在返回前要置为无信号(即只放行当前线程)
        if(pthread_mutex_unlock(&hevent->mutex) != 0) return STD_ERR_FAIL;
        return STD_ERR_OK;
    }
}

void unios_event_destroy(unios_event_t *pevent /*in,out*/) {
    if(!pevent || !(*pevent)) return;

    event_t* hevent = (event_t*)(*pevent);
    pthread_cond_destroy(&hevent->cond);
    pthread_mutex_destroy(&hevent->mutex);
    free(hevent);
    *pevent = NULL;
}
